#pragma once

#include "online.hpp"
#include "db.hpp"
#include "logger.hpp"

#define BOARD_COL 15
#define BOARD_ROW 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2
typedef enum
{
    GAME_START,
    GAME_OVER
} room_statu;

class room
{
public:
    room(uint64_t roomid, user_table *tb_user, online_manager *online_user)
        : _room_id(roomid),
          _statu(GAME_START),
          _player_count(0),
          _tb_user(tb_user),
          _online_user(online_user),
          _board(BOARD_ROW, std::vector<int>(BOARD_COL, 0))
    {
        DBG_LOG("%lu 房间创建成功！！", _room_id);
    }
    uint64_t GetRoomId() { return _room_id; }
    room_statu GetRoomStatu() { return _statu; }
    int GetPlayerCount() { return _player_count; }
    void AddWhiteUser(uint64_t uid)
    {
        _white_id = uid;
        _player_count++;
    }
    void AddBlackUser(uint64_t uid)
    {
        _black_id = uid;
        _player_count++;
    }
    uint64_t GetWhiteId() { return _white_id; }
    uint64_t GetBlackId() { return _black_id; }

    // 下棋的请求
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value json_resp = req;
        // json_resp["optype"] = "put_chess";//req中有

        // 1.当前请求的房间号是否与当前房间的房间号所匹配
        // uint64_t room_id = req["room_id"].asInt64();
        // if (room_id != _room_id)
        // {
        //     json_resp["optype"] = req["optype"].asString();
        //     json_resp["result"] = false;
        //     json_resp["reason"] = "房间号不匹配";
        //     return json_resp;
        // }
        // 2.判断当前的两个用户是不是都在线
        uint64_t cur_uid = req["uid"].asUInt64();
        int row = req["row"].asInt();
        int col = req["col"].asInt();
        // if (!_online_user->is_in_game_room(cur_uid))
        // {
        //     json_resp["optype"] = "put_chess";
        //     json_resp["result"] = true;
        //     json_resp["reason"] = "对方掉线，你赢了";
        //     json_resp["room_id"] = _room_id;
        //     json_resp["uid"] = cur_uid;
        //     json_resp["row"] = row;
        //     json_resp["col"] = col;
        //     json_resp["winner"] = cur_uid == _white_id ? _black_id : _white_id;
        //     return json_resp;
        // }
        if (!_online_user->is_in_game_room(_white_id))
        {
            json_resp["result"] = true;
            json_resp["reason"] = "对方掉线，你赢了";
            // json_resp["room_id"] = _room_id;
            // json_resp["uid"] = cur_uid;
            // json_resp["row"] = row;
            // json_resp["col"] = col;
            json_resp["winner"] = static_cast<Json::UInt64>(_black_id);
            // _tb_user->win(_black_id);
            // _tb_user->lose(_white_id);
            return json_resp;
        }
        if (!_online_user->is_in_game_room(_black_id))
        {
            json_resp["result"] = true;
            json_resp["reason"] = "对方掉线，你赢了";
            json_resp["winner"] = static_cast<Json::UInt64>(_white_id);
            return json_resp;
        }
        // 3.判断走棋的位置，判断是否合理
        if (_board[row][col] != 0)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "当前位置有其他棋子";
            return json_resp;
        }
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[row][col] = cur_color;
        // 4.判断是否有玩家胜利
        // 这里单独封装一个函数
        uint64_t win_id = check_win(row, col, cur_color);

        // 4.1更新数据库信息
        if (win_id != 0)
        {
            // uint64_t loser_id = win_id == _white_id ? _black_id : _white_id;
            // _tb_user->win(win_id);
            // _tb_user->lose(loser_id);

            // 有人胜利了再去设置这个
            json_resp["reason"] = "五星连珠，你赢了";
        }

        json_resp["result"] = true;
        // json_resp["reason"] = "五星连珠，你赢了";
        // json_resp["room_id"] = _room_id;
        // json_resp["uid"] = cur_uid;
        // json_resp["row"] = row;
        // json_resp["col"] = col;
        json_resp["winner"] = static_cast<Json::UInt64>(win_id);
        return json_resp;
    }
    // 聊天的请求
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value json_resp = req;
        json_resp["optype"] = "chat";
        // 1.检查当前房间号是否一致

        // 2.检查敏感词
        std::string msg = req["message"].asString();
        size_t pos = msg.find("垃圾");
        if (pos != std::string::npos)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "消息中不能有敏感词,无法发送";
            return json_resp;
        }
        // 广播消息
        json_resp["result"] = true;
        return json_resp;
    }
    // 退出的请求
    void handle_exit(uint64_t uid)
    {
        Json::Value json_resp;
        // 下棋时退出了 还是 下棋结束时退出
        if (_statu == GAME_START)
        {
            uint64_t cur_uid = uid == _white_id ? _black_id : _white_id;
            json_resp["optype"] = "put_chess";
            json_resp["result"] = true;
            json_resp["reason"] = "对方掉线，你赢了";
            json_resp["room_id"] = static_cast<Json::UInt64>(_room_id);
            json_resp["uid"] = static_cast<Json::UInt64>(cur_uid);
            json_resp["row"] = -1;
            json_resp["col"] = -1;
            json_resp["winner"] = static_cast<Json::UInt64>(cur_uid);

            uint64_t winner_id = json_resp["winner"].asInt64();
            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _statu = GAME_OVER;
            
            broadcast(json_resp);
        }

        // 房间中的玩家数量--
        _player_count--;
    }
    // 广播给所有的玩家
    void broadcast(Json::Value &resp)
    {
        // 1.序列化为字符串
        std::string body;
        json_util::Serialize(resp, body);
        // 2.获取房间中的所有玩家的链接
        // 3.发送响应信息

        // 获取白棋ID
        wsserver_t::connection_ptr wconn = _online_user->get_conn_from_room(_white_id);
        if (wconn.get() != nullptr)
        {
            wconn->send(body);
        }
        // 获取黑旗ID
        wconn = _online_user->get_conn_from_room(_black_id);
        if (wconn.get() != nullptr)
        {
            wconn->send(body);
        }
        return;
    }
    // 一层封装，判断是什么请求调用什么函数
    void handle_request(Json::Value &req)
    {
        // 1.检查房间号是否匹配
        Json::Value json_resp;
        uint64_t room_id = req["room_id"].asInt64();
        if (room_id != _room_id)
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "房间号不匹配";
            broadcast(json_resp);
            return;
        }
        // 2.根据是什么样的请求类型调用不同的请求处理函数
        if (req["optype"].asString() == "put_chess")
        {
            json_resp = handle_chess(req);
            if (json_resp["winner"].asInt64() != 0)
            {
                uint64_t winner_id = json_resp["winner"].asInt64();
                uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _statu = GAME_OVER;
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            json_resp = handle_chat(req);
        }
        else
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "未知请求类型";
        }
        broadcast(json_resp);
        return;
    }

    ~room()
    {
        DBG_LOG("%lu 房间销毁成功！！", _room_id);
    }

private:
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        int count = 1;
        int search_row = row + row_off;
        int search_col = col + col_off;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            // 同色棋子加加
            count++;
            // 继续向后偏移
            search_row += row_off;
            search_col += col_off;
        }
        // 相反的方向再来一次
        search_row = row - row_off;
        search_col = col - col_off;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            // 同色棋子加加
            count++;
            // 继续向后偏移
            search_row -= row_off;
            search_col -= col_off;
        }
        return (count >= 5);
    }
    uint64_t check_win(int row, int col, int color)
    {
        if (five(row, col, 0, 1, color) ||
            five(row, col, 1, 0, color) ||
            five(row, col, -1, 1, color) ||
            five(row, col, -1, -1, color))
        {
            // 任意方向出现了五星连珠
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }

private:
    uint64_t _room_id;            // 房间id
    room_statu _statu;            // 房间状态
    int _player_count;            // 房间的用户的数量
    uint64_t _white_id;           // 白方id
    uint64_t _black_id;           // 黑方id
    user_table *_tb_user;         // 用户信息的表的句柄
    online_manager *_online_user; // 在线用户的管理句柄
    std::vector<std::vector<int>> _board;
};

using room_ptr = std::shared_ptr<room>;

class room_manager
{
public:
    room_manager(user_table *ut, online_manager *om)
        : _next_rid(1),
          _tb_user(ut),
          _online_user(om)
    {
        DBG_LOG("房间管理模块初始化完毕");
    }
    ~room_manager()
    {
        DBG_LOG("房间管理模块销毁成功");
    }
    // 创建房间，返回房间的指针管理对象
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        // 1.只有两个玩家都在游戏大厅的话才会去创建房间
        if (!_online_user->is_in_game_hall(uid1))
        {
            DBG_LOG("创建房间失败，%lu 掉线", uid1);
            return room_ptr();
        }
        if (!_online_user->is_in_game_hall(uid2))
        {
            DBG_LOG("创建房间失败，%lu 掉线", uid2);
            return room_ptr();
        }
        // 2.创建房间，将用户信息添加到游戏房间中去
        std::unique_lock<std::mutex> lock(_mutex);

        room_ptr rp(new room(_next_rid, _tb_user, _online_user));
        rp->AddWhiteUser(uid1);
        rp->AddBlackUser(uid2);
        // 3.将房间信息管理起来
        _rooms.insert(std::make_pair(_next_rid, rp));
        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));
        _next_rid++;
        // 4.返回房间信息
        return rp;
    }

    // 通过房间id查找房间
    room_ptr get_room_by_rid(uint64_t rid)
    {
        // #ifndef LOCK
        std::unique_lock<std::mutex> lock(_mutex);
        // #endif
        auto it = _rooms.find(rid);
        if (it == _rooms.end())
        {
            return room_ptr();
        }
        return it->second;
    }
    // 通过用户id查找房间
    room_ptr get_room_by_uid(uint64_t uid)
    {
        // 先找到房间id
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _users.find(uid);
        if (it == _users.end())
        {
            return room_ptr();
        }
        // 再去找房间
        uint64_t rid = it->second;
        // 2. 通过房间ID获取房间信息
        auto rit = _rooms.find(rid);
        if (rit == _rooms.end())
        {
            return room_ptr();
        }
        return rit->second;
        //         // 使用条件编译防止锁冲突
        // #define LOCK
        //         room_ptr result = get_room_by_rid(it->second);
        // #undef LOCK // 及时取消宏定义，避免影响后续代码
        //         return result;
    }
    // 删除房间
    void remove_room(uint64_t rid)
    {
        // 房间信息是通过shared_ptr在_rooms里管理，因此只需要将shared_ptr从_rooms中移除
        // 外界没有人对房间进行操作，shared_ptr计数器==0
        // 1. 通过房间ID获取房间信息
        room_ptr rp = get_room_by_rid(rid);
        if (rp.get() == nullptr)
        {
            return;
        }
        // 2. 通过房间信息获取房间中所有的用户ID
        uint64_t uid1 = rp->GetWhiteId();
        uint64_t uid2 = rp->GetBlackId();
        // 3. 移除房间中的用户信息
        std::unique_lock<std::mutex> lock(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);
        // 4. 移除房间管理信息
        _rooms.erase(rid);
    }
    // 删除房间里的用户
    void remove_room_user(uint64_t uid)
    {
        // 1. 先获取房间信息
        room_ptr rp = get_room_by_uid(uid);
        if (rp.get() == nullptr)
        {
            return;
        }
        // 2. 玩家退出
        rp->handle_exit(uid);
        // 3. 房间没有玩家了，销毁房间
        if (rp->GetPlayerCount() == 0)
        {
            remove_room(rp->GetRoomId());
        }
    }

private:
    uint64_t _next_rid;
    std::mutex _mutex;
    user_table *_tb_user;                          // 用户信息的管理
    online_manager *_online_user;                  // 在线用户信息的管理
    std::unordered_map<uint64_t, room_ptr> _rooms; // 房间id与房间的映射
    std::unordered_map<uint64_t, uint64_t> _users; // 用户与房间id的映射
};